From 34e859a5bc65744f34bcb9a594b24fc5d764ecea Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 15 Mar 2019 10:34:06 -0400 Subject: [PATCH] Add a tagged entry demo This can serve as an example for how composite entries can now be done outside of GTK, easily. --- demos/gtk-demo/demo.gresource.xml | 8 + demos/gtk-demo/demotaggedentry.c | 493 ++++++++++++++++++++++++++++++ demos/gtk-demo/demotaggedentry.h | 89 ++++++ demos/gtk-demo/meson.build | 9 +- demos/gtk-demo/tagged_entry.c | 112 +++++++ demos/gtk-demo/tagstyle.css | 19 ++ 6 files changed, 729 insertions(+), 1 deletion(-) create mode 100644 demos/gtk-demo/demotaggedentry.c create mode 100644 demos/gtk-demo/demotaggedentry.h create mode 100644 demos/gtk-demo/tagged_entry.c create mode 100644 demos/gtk-demo/tagstyle.css diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index fa0c245c87..cdf86c3c35 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -207,6 +207,7 @@ spinbutton.c spinner.c tabs.c + tagged_entry.c textview.c textscroll.c theming_style_classes.c @@ -218,6 +219,9 @@ floppybuddy.gif + + tagstyle.css + listbox.ui messages.txt @@ -262,6 +266,10 @@ dnd.css + + demotaggedentry.c + demotaggedentry.h + icons/16x16/actions/application-exit.png icons/16x16/actions/document-new.png diff --git a/demos/gtk-demo/demotaggedentry.c b/demos/gtk-demo/demotaggedentry.c new file mode 100644 index 0000000000..3f9f63a437 --- /dev/null +++ b/demos/gtk-demo/demotaggedentry.c @@ -0,0 +1,493 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2019 Red Hat, Inc. + * + * Authors: + * - Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "demotaggedentry.h" + +#include +#include + +typedef struct { + GtkWidget *box; + GtkWidget *entry; +} DemoTaggedEntryPrivate; + +static void demo_tagged_entry_editable_init (GtkEditableInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (DemoTaggedEntry, demo_tagged_entry, GTK_TYPE_WIDGET, + G_ADD_PRIVATE (DemoTaggedEntry) + G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, demo_tagged_entry_editable_init)) + +static void +demo_tagged_entry_init (DemoTaggedEntry *entry) +{ + DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry); + + gtk_widget_set_has_surface (GTK_WIDGET (entry), FALSE); + + priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_parent (priv->box, GTK_WIDGET (entry)); + + priv->entry = gtk_text_new (); + gtk_widget_set_hexpand (priv->entry, TRUE); + gtk_widget_set_vexpand (priv->entry, TRUE); + gtk_widget_set_hexpand (priv->box, FALSE); + gtk_widget_set_vexpand (priv->box, FALSE); + gtk_container_add (GTK_CONTAINER (priv->box), priv->entry); + gtk_editable_init_delegate (GTK_EDITABLE (entry)); +} + +static void +demo_tagged_entry_dispose (GObject *object) +{ + DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (object); + DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry); + + if (priv->entry) + gtk_editable_finish_delegate (GTK_EDITABLE (entry)); + + g_clear_pointer (&priv->entry, gtk_widget_unparent); + g_clear_pointer (&priv->box, gtk_widget_unparent); + + G_OBJECT_CLASS (demo_tagged_entry_parent_class)->dispose (object); +} + +static void +demo_tagged_entry_finalize (GObject *object) +{ + G_OBJECT_CLASS (demo_tagged_entry_parent_class)->finalize (object); +} + +static void +demo_tagged_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + if (gtk_editable_delegate_set_property (object, prop_id, value, pspec)) + return; + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +demo_tagged_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + if (gtk_editable_delegate_get_property (object, prop_id, value, pspec)) + return; + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +demo_tagged_entry_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (widget); + DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry); + + gtk_widget_measure (priv->box, orientation, for_size, + minimum, natural, + minimum_baseline, natural_baseline); +} + +static void +demo_tagged_entry_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (widget); + DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry); + + gtk_widget_size_allocate (priv->box, + &(GtkAllocation) { 0, 0, width, height }, + baseline); +} + +static void +demo_tagged_entry_grab_focus (GtkWidget *widget) +{ + DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (widget); + DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry); + + gtk_widget_grab_focus (priv->entry); +} + +static void +demo_tagged_entry_class_init (DemoTaggedEntryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = demo_tagged_entry_dispose; + object_class->finalize = demo_tagged_entry_finalize; + object_class->get_property = demo_tagged_entry_get_property; + object_class->set_property = demo_tagged_entry_set_property; + + widget_class->measure = demo_tagged_entry_measure; + widget_class->size_allocate = demo_tagged_entry_size_allocate; + widget_class->grab_focus = demo_tagged_entry_grab_focus; + + gtk_editable_install_properties (object_class, 1); + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE); + gtk_widget_class_set_css_name (widget_class, "entry"); +} + +static GtkEditable * +demo_tagged_entry_get_delegate (GtkEditable *editable) +{ + DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (editable); + DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry); + + return GTK_EDITABLE (priv->entry); +} + +static void +demo_tagged_entry_editable_init (GtkEditableInterface *iface) +{ + iface->get_delegate = demo_tagged_entry_get_delegate; +} + +GtkWidget * +demo_tagged_entry_new (void) +{ + return GTK_WIDGET (g_object_new (DEMO_TYPE_TAGGED_ENTRY, NULL)); +} + +void +demo_tagged_entry_add_tag (DemoTaggedEntry *entry, + GtkWidget *tag) +{ + DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry); + + g_return_if_fail (DEMO_IS_TAGGED_ENTRY (entry)); + + gtk_container_add (GTK_CONTAINER (priv->box), tag); +} + +void +demo_tagged_entry_insert_tag_after (DemoTaggedEntry *entry, + GtkWidget *tag, + GtkWidget *sibling) +{ + DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry); + + g_return_if_fail (DEMO_IS_TAGGED_ENTRY (entry)); + + if (sibling == NULL) + gtk_container_add (GTK_CONTAINER (priv->box), tag); + else + gtk_box_insert_child_after (GTK_BOX (priv->box), tag, sibling); +} + +void +demo_tagged_entry_remove_tag (DemoTaggedEntry *entry, + GtkWidget *tag) +{ + DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry); + + g_return_if_fail (DEMO_IS_TAGGED_ENTRY (entry)); + + gtk_container_remove (GTK_CONTAINER (priv->box), tag); +} + +struct _DemoTaggedEntryTag +{ + GtkWidget parent; + GtkWidget *box; + GtkWidget *label; + GtkWidget *button; + + gboolean has_close_button; + char *style; +}; + +struct _DemoTaggedEntryTagClass +{ + GtkWidgetClass parent_class; +}; + +enum { + PROP_0, + PROP_LABEL, + PROP_HAS_CLOSE_BUTTON, +}; + +enum { + SIGNAL_CLICKED, + SIGNAL_BUTTON_CLICKED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + +G_DEFINE_TYPE (DemoTaggedEntryTag, demo_tagged_entry_tag, GTK_TYPE_WIDGET) + +static void +on_released (GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + DemoTaggedEntryTag *tag) +{ + g_signal_emit (tag, signals[SIGNAL_CLICKED], 0); +} + +static void +demo_tagged_entry_tag_init (DemoTaggedEntryTag *tag) +{ + GtkGesture *gesture; + GtkCssProvider *provider; + + gtk_widget_set_has_surface (GTK_WIDGET (tag), FALSE); + + tag->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_parent (tag->box, GTK_WIDGET (tag)); + tag->label = gtk_label_new (""); + gtk_container_add (GTK_CONTAINER (tag->box), tag->label); + + gesture = gtk_gesture_multi_press_new (); + g_signal_connect (gesture, "released", G_CALLBACK (on_released), tag); + gtk_widget_add_controller (GTK_WIDGET (tag), GTK_EVENT_CONTROLLER (gesture)); + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (provider, "/tagged_entry/tagstyle.css"); + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + 800); + g_object_unref (provider); +} + +static void +demo_tagged_entry_tag_dispose (GObject *object) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object); + + g_clear_pointer (&tag->box, gtk_widget_unparent); + + G_OBJECT_CLASS (demo_tagged_entry_tag_parent_class)->dispose (object); +} + +static void +demo_tagged_entry_tag_finalize (GObject *object) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object); + + g_clear_pointer (&tag->box, gtk_widget_unparent); + g_clear_pointer (&tag->style, g_free); + + G_OBJECT_CLASS (demo_tagged_entry_tag_parent_class)->finalize (object); +} + +static void +demo_tagged_entry_tag_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object); + + switch (prop_id) + { + case PROP_LABEL: + demo_tagged_entry_tag_set_label (tag, g_value_get_string (value)); + break; + + case PROP_HAS_CLOSE_BUTTON: + demo_tagged_entry_tag_set_has_close_button (tag, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +demo_tagged_entry_tag_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object); + + switch (prop_id) + { + case PROP_LABEL: + g_value_set_string (value, demo_tagged_entry_tag_get_label (tag)); + break; + + case PROP_HAS_CLOSE_BUTTON: + g_value_set_boolean (value, demo_tagged_entry_tag_get_has_close_button (tag)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +demo_tagged_entry_tag_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (widget); + + gtk_widget_measure (tag->box, orientation, for_size, + minimum, natural, + minimum_baseline, natural_baseline); +} + +static void +demo_tagged_entry_tag_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (widget); + + gtk_widget_size_allocate (tag->box, + &(GtkAllocation) { 0, 0, width, height }, + baseline); +} + +static void +demo_tagged_entry_tag_class_init (DemoTaggedEntryTagClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + object_class->dispose = demo_tagged_entry_tag_dispose; + object_class->finalize = demo_tagged_entry_tag_finalize; + object_class->set_property = demo_tagged_entry_tag_set_property; + object_class->get_property = demo_tagged_entry_tag_get_property; + + widget_class->measure = demo_tagged_entry_tag_measure; + widget_class->size_allocate = demo_tagged_entry_tag_size_allocate; + + signals[SIGNAL_CLICKED] = + g_signal_new ("clicked", + DEMO_TYPE_TAGGED_ENTRY_TAG, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + signals[SIGNAL_BUTTON_CLICKED] = + g_signal_new ("button-clicked", + DEMO_TYPE_TAGGED_ENTRY_TAG, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + g_object_class_install_property (object_class, PROP_LABEL, + g_param_spec_string ("label", "Label", "Label", + NULL, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_HAS_CLOSE_BUTTON, + g_param_spec_boolean ("has-close-button", "Has close button", "Whether this tag has a close button", + FALSE, G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + + gtk_widget_class_set_css_name (widget_class, "tag"); +} + +DemoTaggedEntryTag * +demo_tagged_entry_tag_new (const char *label) +{ + return DEMO_TAGGED_ENTRY_TAG (g_object_new (DEMO_TYPE_TAGGED_ENTRY_TAG, + "label", label, + NULL)); +} + +const char * +demo_tagged_entry_tag_get_label (DemoTaggedEntryTag *tag) +{ + g_return_val_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag), NULL); + + return gtk_label_get_label (GTK_LABEL (tag->label)); +} + +void +demo_tagged_entry_tag_set_label (DemoTaggedEntryTag *tag, + const char *label) +{ + g_return_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag)); + + gtk_label_set_label (GTK_LABEL (tag->label), label); +} + +static void +on_button_clicked (GtkButton *button, + DemoTaggedEntryTag *tag) +{ + g_signal_emit (tag, signals[SIGNAL_BUTTON_CLICKED], 0); +} + +void +demo_tagged_entry_tag_set_has_close_button (DemoTaggedEntryTag *tag, + gboolean has_close_button) +{ + g_return_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag)); + + if ((tag->button != NULL) == has_close_button) + return; + + if (!has_close_button && tag->button) + { + gtk_container_remove (GTK_CONTAINER (tag->box), tag->button); + tag->button = NULL; + } + else if (has_close_button && tag->button == NULL) + { + GtkWidget *image; + + image = gtk_image_new_from_icon_name ("window-close-symbolic"); + tag->button = gtk_button_new (); + gtk_container_add (GTK_CONTAINER (tag->button), image); + gtk_widget_set_halign (tag->button, GTK_ALIGN_CENTER); + gtk_widget_set_valign (tag->button, GTK_ALIGN_CENTER); + gtk_button_set_relief (GTK_BUTTON (tag->button), GTK_RELIEF_NONE); + gtk_container_add (GTK_CONTAINER (tag->box), tag->button); + g_signal_connect (tag->button, "clicked", G_CALLBACK (on_button_clicked), tag); + } + + g_object_notify (G_OBJECT (tag), "has-close-button"); +} + +gboolean +demo_tagged_entry_tag_get_has_close_button (DemoTaggedEntryTag *tag) +{ + g_return_val_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag), FALSE); + + return tag->button != NULL; +} diff --git a/demos/gtk-demo/demotaggedentry.h b/demos/gtk-demo/demotaggedentry.h new file mode 100644 index 0000000000..aef2c34509 --- /dev/null +++ b/demos/gtk-demo/demotaggedentry.h @@ -0,0 +1,89 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2019 Red Hat, Inc. + * + * Authors: + * - Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __DEMO_TAGGED_ENTRY_H__ +#define __DEMO_TAGGED_ENTRY_H__ + +#include + +G_BEGIN_DECLS + +#define DEMO_TYPE_TAGGED_ENTRY (demo_tagged_entry_get_type ()) +#define DEMO_TAGGED_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEMO_TYPE_TAGGED_ENTRY, DemoTaggedEntry)) +#define DEMO_TAGGED_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DEMO_TYPE_TAGGED_ENTRY, DemoTaggedEntryClass)) +#define DEMO_IS_TAGGED_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEMO_TYPE_TAGGED_ENTRY)) +#define DEMO_IS_TAGGED_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DEMO_TYPE_TAGGED_ENTRY)) +#define DEMO_TAGGED_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DEMO_TYPE_TAGGED_ENTRY, DemoTaggedEntryClass)) + +typedef struct _DemoTaggedEntry DemoTaggedEntry; +typedef struct _DemoTaggedEntryClass DemoTaggedEntryClass; + +struct _DemoTaggedEntry +{ + GtkWidget parent; +}; + +struct _DemoTaggedEntryClass +{ + GtkWidgetClass parent_class; +}; + +#define DEMO_TYPE_TAGGED_ENTRY_TAG (demo_tagged_entry_tag_get_type ()) +#define DEMO_TAGGED_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEMO_TYPE_TAGGED_ENTRY_TAG, DemoTaggedEntryTag)) +#define DEMO_TAGGED_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DEMO_TYPE_TAGGED_ENTRY_TAG, DemoTaggedEntryTag)) +#define DEMO_IS_TAGGED_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEMO_TYPE_TAGGED_ENTRY_TAG)) +#define DEMO_IS_TAGGED_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DEMO_TYPE_TAGGED_ENTRY_TAG)) +#define DEMO_TAGGED_ENTRY_TAG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DEMO_TYPE_TAGGED_ENTRY_TAG, DemoTaggedEntryTagClass)) + +typedef struct _DemoTaggedEntryTag DemoTaggedEntryTag; +typedef struct _DemoTaggedEntryTagClass DemoTaggedEntryTagClass; + + +GType demo_tagged_entry_get_type (void) G_GNUC_CONST; +GType demo_tagged_entry_tag_get_type (void) G_GNUC_CONST; + +GtkWidget * demo_tagged_entry_new (void); + +void demo_tagged_entry_add_tag (DemoTaggedEntry *entry, + GtkWidget *tag); + +void demo_tagged_entry_insert_tag_after (DemoTaggedEntry *entry, + GtkWidget *tag, + GtkWidget *sibling); + +void demo_tagged_entry_remove_tag (DemoTaggedEntry *entry, + GtkWidget *tag); + +DemoTaggedEntryTag * + demo_tagged_entry_tag_new (const char *label); + +const char * demo_tagged_entry_tag_get_label (DemoTaggedEntryTag *tag); + +void demo_tagged_entry_tag_set_label (DemoTaggedEntryTag *tag, + const char *label); + +gboolean demo_tagged_entry_tag_get_has_close_button (DemoTaggedEntryTag *tag); + +void demo_tagged_entry_tag_set_has_close_button (DemoTaggedEntryTag *tag, + gboolean has_close_button); + +G_END_DECLS + +#endif /* __DEMO_TAGGED_ENTRY_H__ */ diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build index e331001abb..86fd6d6b80 100644 --- a/demos/gtk-demo/meson.build +++ b/demos/gtk-demo/meson.build @@ -65,6 +65,7 @@ demos = files([ 'spinner.c', 'stack.c', 'tabs.c', + 'tagged_entry.c', 'textmask.c', 'textview.c', 'textscroll.c', @@ -76,7 +77,13 @@ demos = files([ gtkdemo_deps = [ libgtk_dep, ] -extra_demo_sources = files(['main.c', 'gtkfishbowl.c', 'fontplane.c', 'gtkgears.c', 'puzzlepiece.c', 'bluroverlay.c']) +extra_demo_sources = files(['main.c', + 'gtkfishbowl.c', + 'fontplane.c', + 'gtkgears.c', + 'puzzlepiece.c', + 'bluroverlay.c', + 'demotaggedentry.c']) if harfbuzz_dep.found() and pangoft_dep.found() demos += files('font_features.c') diff --git a/demos/gtk-demo/tagged_entry.c b/demos/gtk-demo/tagged_entry.c new file mode 100644 index 0000000000..6405d19643 --- /dev/null +++ b/demos/gtk-demo/tagged_entry.c @@ -0,0 +1,112 @@ +/* Entry/Tagged Entry + * + * This example shows how to build a complex composite + * entry using GtkText, outside of GTK. + * + * This tagged entry can display tags and other widgets + * inside the entry area. + */ + +#include +#include +#include "demotaggedentry.h" + +static GtkWidget *spinner = NULL; + +static void +closed_cb (DemoTaggedEntryTag *tag, DemoTaggedEntry *entry) +{ + demo_tagged_entry_remove_tag (entry, GTK_WIDGET (tag)); +} + +static void +add_tag (GtkButton *button, DemoTaggedEntry *entry) +{ + DemoTaggedEntryTag *tag; + + tag = demo_tagged_entry_tag_new ("Blue"); + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tag)), "blue"); + demo_tagged_entry_tag_set_has_close_button (tag, TRUE); + g_signal_connect (tag, "button-clicked", G_CALLBACK (closed_cb), entry); + + if (spinner == NULL) + demo_tagged_entry_add_tag (entry, GTK_WIDGET (tag)); + else + demo_tagged_entry_insert_tag_after (entry, GTK_WIDGET (tag), gtk_widget_get_prev_sibling (spinner)); +} + +static void +toggle_spinner (GtkCheckButton *button, DemoTaggedEntry *entry) +{ + if (spinner) + { + demo_tagged_entry_remove_tag (entry, spinner); + spinner = NULL; + } + else + { + spinner = gtk_spinner_new (); + gtk_spinner_start (GTK_SPINNER (spinner)); + demo_tagged_entry_add_tag (entry, spinner); + } +} + +GtkWidget * +do_tagged_entry (GtkWidget *do_widget) +{ + static GtkWidget *window = NULL; + GtkWidget *box; + GtkWidget *box2; + GtkWidget *header; + GtkWidget *entry; + GtkWidget *button; + + if (!window) + { + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_display (GTK_WINDOW (window), + gtk_widget_get_display (do_widget)); + header = gtk_header_bar_new (); + gtk_header_bar_set_title (GTK_HEADER_BAR (header), "A tagged entry"); + gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), FALSE); + gtk_window_set_titlebar (GTK_WINDOW (window), header); + gtk_window_set_resizable (GTK_WINDOW (window), TRUE); + gtk_window_set_deletable (GTK_WINDOW (window), FALSE); + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), &window); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + g_object_set (box, "margin", 18, NULL); + gtk_container_add (GTK_CONTAINER (window), box); + + entry = demo_tagged_entry_new (); + gtk_container_add (GTK_CONTAINER (box), entry); + + box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_set_halign (box2, GTK_ALIGN_END); + gtk_container_add (GTK_CONTAINER (box), box2); + + button = gtk_button_new_with_mnemonic ("Add _Tag"); + g_signal_connect (button, "clicked", G_CALLBACK (add_tag), entry); + gtk_container_add (GTK_CONTAINER (box2), button); + + button = gtk_check_button_new_with_mnemonic ("_Spinner"); + g_signal_connect (button, "toggled", G_CALLBACK (toggle_spinner), entry); + gtk_container_add (GTK_CONTAINER (box2), button); + + button = gtk_button_new_with_mnemonic ("_Done"); + gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action"); + g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window); + gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button); + + gtk_widget_set_can_default (button, TRUE); + gtk_window_set_default (GTK_WINDOW (window), button); + } + + if (!gtk_widget_get_visible (window)) + gtk_widget_show (window); + else + gtk_widget_destroy (window); + + return window; +} diff --git a/demos/gtk-demo/tagstyle.css b/demos/gtk-demo/tagstyle.css new file mode 100644 index 0000000000..c470a5e2e2 --- /dev/null +++ b/demos/gtk-demo/tagstyle.css @@ -0,0 +1,19 @@ +tag { + margin: 4px; + padding: 4px; + border-radius: 4px; + background: lightskyblue; +} +tag box { + border-spacing: 4px; +} +tag label, +tag image { + color: white; +} +tag button { + min-height: 0; + min-width: 0; + padding: 0; + border: 1px solid white; +} -- 2.30.2